package jcu.sal.client.gui.view;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.rmi.RemoteException;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import jcu.sal.client.gui.ClientController;
import jcu.sal.client.gui.ClientControllerImpl;
import jcu.sal.common.Constants;
import jcu.sal.common.agents.SALAgent;
import jcu.sal.common.events.Event;
import jcu.sal.common.exceptions.ConfigurationException;
import jcu.sal.common.exceptions.NotFoundException;

public class ClientViewImpl extends AbstractClientView implements ActionListener{
	
	private static String ActionAddRMIAgent = "addRMI";
	private static String ActionRemoveRMIAgent = "removeRMI";
	private static String ActionQuit = "quit";

	private String name;
	private JMenuItem disconnectRMIAgent;
	
	/**
	 * This method creates a new SAL Client view
	 * @param c the SAL client controller managing interactions with different SAL agents
	 * @param n a unique name for this client
	 */
	public ClientViewImpl(ClientController c, String n){
		super(c);
		controller = c;
		name = n;
	}
	
	@Override
	public void release(){
		//unregister event handlers and release all agents
		for(SALAgent a : tree.getAgents()){
			unregisterEventhandlers(a);
			controller.disconnect(a);
		}
	}
	
	/**
	 * this method registers this client for all {@link Event}s
	 * generated by the given SALAgent.  
	 * @param a the SAL agent whose {@link Event}s we want to register for
	 * @throws NotFoundException if there is an error registering
	 */
	public void registerEventHandlers(SALAgent a) throws NotFoundException{
		controller.registerEventHandler(a, this, Constants.PROTOCOL_MANAGER_PRODUCER_ID);
		controller.registerEventHandler(a, this, Constants.SENSOR_MANAGER_PRODUCER_ID);
		controller.registerEventHandler(a, this, Constants.SENSOR_STATE_PRODUCER_ID);
	}
	
	/**
	 * This method unregisters the event handlers previously registered
	 * with {@link #registerEventHandlers(SALAgent)}.
	 * @param a the SAL agent who we want to unregister from
	 */
	public void unregisterEventhandlers(SALAgent a){
		try {
			controller.unregisterEventHandler(a, this, Constants.PROTOCOL_MANAGER_PRODUCER_ID);
			controller.unregisterEventHandler(a, this, Constants.SENSOR_MANAGER_PRODUCER_ID);
			controller.unregisterEventHandler(a, this, Constants.SENSOR_STATE_PRODUCER_ID);
		} catch (Throwable e1) {
			e1.printStackTrace();
		} 
	}
	
	/**
	 * This method is called by {@link SensorTree} when a component (sensor, protocol, agent)
	 * has been selected
	 * @param c the {@link Context} of the selected object
	 */
	@Override
	public void componentSelected(Context c){
		disconnectRMIAgent.setEnabled(false);
		if(c.getType()==Context.ROOT_NODE)
			addLog("Root Node '"+c.toString()+"' selected");
		else if(c.getType()==Context.AGENT_TYPE) {
			if(c.getAgent().getType().equals(Constants.RMI_Agent_type))
				disconnectRMIAgent.setEnabled(true);
			addLog("Agent Node '"+c.toString()+"' selected");
		}else if(c.getType()==Context.PROTOCOL_TYPE)
			addLog("Protocol '"+c.toString()+"' selected");
		else if(c.getType()==Context.SENSOR_TYPE)
			actionPane.displaySensor(c);
		else
			addLog("Unknown element type");
		
		actionPane.getPanel().revalidate();
		actionPane.getPanel().repaint();
		
	}
	
	/**
	 * This method is called when the user selected the "Connect to RMI Agent" menu item.
	 * It asks the user for the IP addresses of RMI registries, creates the client stub
	 * and updates the GUI 
	 */
	public void addRmiAgent(){
		Vector<String> v;
		try {
			v = new Vector<String>();
			Enumeration<NetworkInterface> i = NetworkInterface.getNetworkInterfaces();
			Enumeration<InetAddress> add;
			while(i.hasMoreElements()){
				add = i.nextElement().getInetAddresses();
				while(add.hasMoreElements())
					v.add(add.nextElement().getHostAddress());
			}
				
		} catch (SocketException e1) {
			JOptionPane.showMessageDialog(frame,
				    "Unable to list the network interfaces.",
				    "Network error",
				    JOptionPane.ERROR_MESSAGE);
			return;
		}
		String[] ifs = new String[v.size()];		
		v.copyInto(ifs);
		
		String agentIP = (String)JOptionPane.showInputDialog(
                frame,
                "Enter the IP address of the RMI SAL agent:\n",
                "SAL Agent detail",
                JOptionPane.QUESTION_MESSAGE,
                null,
                null,
                "127.0.0.1");
		if(agentIP==null)
			return;
		
		String ourIP = (String)JOptionPane.showInputDialog(
                frame,
                "Enter the IP address of this client's RMI registry:\n",
                "SAL Agent detail",
                JOptionPane.QUESTION_MESSAGE,
                null,
                ifs,
                //null,
                "127.0.0.1");
		
		if(ourIP!=null){
			try {
				SALAgent a = controller.rmiConnect(name, agentIP, ourIP);
				registerEventHandlers(a);
				tree.addAgent(a);
			} catch (ConfigurationException e) {
				addLog("Chosen RMI name '"+name+"' already registered with agent");
				e.printStackTrace();
			} catch (RemoteException e) {
				addLog("RMI error while connecting to agent");
				e.printStackTrace();
			} catch (NotFoundException e) {
				addLog("Error registering event handlers with agent");
				e.printStackTrace();
			}
			
		}
	}
	
	public void removeRmiAgent(SALAgent a){
		disconnectRMIAgent.setEnabled(false);
		tree.removeAgent(a);
		actionPane.displaySensor(null);
		unregisterEventhandlers(a);
		controller.rmiDisonnect(a);
	}
	
	/**
	 * This method is invoked by swing whenever an item in the menu bar is selected
	 */
	public void actionPerformed(ActionEvent e) {
		if(ActionAddRMIAgent.equals(e.getActionCommand())){
			addRmiAgent();
		} else if(ActionRemoveRMIAgent.equals(e.getActionCommand())){
			removeRmiAgent(tree.getSelectedLabel().getAgent());
		} else if(ActionQuit.equals(e.getActionCommand())){
			//quit
			frame.dispose();
		} 
	}
	
	/**
	 * This method creates the menu bar
	 */
	protected JMenuBar createMenu(){
		JMenuBar bar = new JMenuBar();
		JMenu menu = new JMenu("SAL Client");
		menu.setMnemonic(KeyEvent.VK_S);
		bar.add(menu);
		
		JMenuItem item = new JMenuItem("Add RMI Agent");
		item.setActionCommand(ActionAddRMIAgent);
		item.setMnemonic(KeyEvent.VK_A);
		item.addActionListener(this);
		menu.add(item);
		
		disconnectRMIAgent = new JMenuItem("Disconnect RMI Agent");
		disconnectRMIAgent.setActionCommand(ActionRemoveRMIAgent);
		disconnectRMIAgent.setMnemonic(KeyEvent.VK_D);
		disconnectRMIAgent.addActionListener(this);
		disconnectRMIAgent.setEnabled(false);
		menu.add(disconnectRMIAgent);
		
		menu.addSeparator();
		
		item = new JMenuItem("Quit");
		item.setActionCommand(ActionQuit);
		item.setMnemonic(KeyEvent.VK_Q);
		item.addActionListener(this);
		menu.add(item);		
		
		return bar;
	}

	public static void main(final String[] args) {
		
		ClientController c = new ClientControllerImpl();
		final ClientViewImpl cl = new ClientViewImpl(c, args[0]);
		SwingUtilities.invokeLater(new Runnable() {
			public void run(){
				cl.initGUI();
			}
		});

	}
}
